package cn.buk.tms.dao;

import cn.buk.common.sc.Page;
import cn.buk.tms.sc.SearchCustomerCriteria;
import cn.buk.common.util.DateUtil;
import java.util.ArrayList;
import java.util.List;

import jakarta.persistence.criteria.*;
import jakarta.transaction.Transactional;

import cn.buk.tms.entity.misc.*;
import org.springframework.stereotype.Repository;

import static cn.buk.tms.common.constant.TmsOrderConstant.OPERATING_OBJECT_BASIC_DATA;
import static cn.buk.tms.common.constant.TmsOrderConstant.OPERATING_OBJECT_CUSTOMER_BASIC_DATA;

@Repository
@Transactional
public class PassengerDaoImpl extends AbstractDao implements PassengerDao {

  /**
   * 保存企业乘机人资料
   */
  @Override
  public int create(Passenger passenger) {
    em.persist(passenger);
    em.flush();

    return passenger.getId();
  }

  @Override
  public Passenger getById(int id) {
    return em.find(Passenger.class, id);
  }

  @Override
  public int update(Passenger passenger) {
    int statusCode = 0;
    em.merge(passenger);

    statusCode = passenger.getId();

    return statusCode;
  }

  @Override
  public int delete(Passenger passenger) {
    int statusCode = 0;

    em.remove(passenger);

    statusCode = passenger.getId();

    return statusCode;
  }

  @Override
  public int deletePassengerById(int enterpriseId, int psgId) {
    return em.createQuery("delete from Passenger o " +
        "where o.enterpriseId = :enterpriseId and o.id = :id ")
        .setParameter("enterpriseId", enterpriseId)
        .setParameter("id", psgId)
        .executeUpdate();
  }

  @Override
  public List<Passenger> searchPsgInfoMobileIsNull(int enterpriseId, int max) {
    return em.createQuery("select o from Passenger o " +
            "where o.enterpriseId = :enterpriseId " +
            "and o.mobile is null order by o.id desc", Passenger.class)
            .setParameter("enterpriseId", enterpriseId)
            .setMaxResults(max)
            .getResultList();
  }

  @Override
  public List<Passenger> searchPsgInfoFirstNameIsNull(int enterpriseId, int max) {
    return em.createQuery("select o from Passenger o " +
            "where o.enterpriseId = :enterpriseId " +
            "and o.firstName is null and o.nameEn is not null order by o.id desc", Passenger.class)
            .setParameter("enterpriseId", enterpriseId)
            .setMaxResults(max)
            .getResultList();
  }

  @Override
  public int updatePassengerMobile(int enterpriseId, String username, int psgId, String mobile) {
    Passenger oldPsg = em.find(Passenger.class, psgId);
    int retCode = em.createQuery("update Passenger o set o.mobile = :mobile, o.lastUpdate = :lastUpdate " +
            "where o.id = :id " +
            "and o.enterpriseId = :enterpriseId and o.mobile is null")
            .setParameter("id", psgId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("mobile", mobile)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It's failed to update employee's mobile.");
    }

    OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_CUSTOMER_BASIC_DATA);
    log.setEnterpriseId(enterpriseId);
    log.setOperator(username);
    log.setContent("修改(" + oldPsg.getId() + ", " + oldPsg.getNameCn() + ", " + oldPsg.getMobile() + ")手机号为 " + mobile);
    em.persist(log);

    return retCode;
  }

  @Override
  public int updatePassengerFirstName(int enterpriseId, String username, int psgId, String firstName, String lastName) {
    int retCode = em.createQuery("update Passenger o " +
            "set o.firstName = :firstName, o.lastName = :lastName " +
            "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", psgId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("firstName", firstName)
            .setParameter("lastName", lastName)
            .executeUpdate();
    if (retCode == 1) {
      OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_BASIC_DATA);
      log.setOperator(username);
      log.setContent("修改firstName: " + firstName + ", lastName: " + lastName);
      em.persist(log);
    }

    return retCode;
  }

  @Override
  public int savePassenger2FlightKeyCustomerPolicy(int enterpriseId, String username, int psgId, int policyId) {
    FlightKeyCustomerPolicy flightPolicy = em.find(FlightKeyCustomerPolicy.class, policyId);
    Passenger passenger = em.find(Passenger.class, psgId);

    PassengerFlightPolicy pfp = new PassengerFlightPolicy();
    pfp.setEnterpriseId(enterpriseId);
    pfp.setPassenger(passenger);
    pfp.setPolicyInfo(flightPolicy);

    em.persist(pfp);

    OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_CUSTOMER_BASIC_DATA);
    log.setOperator(username);
    log.setContent(passenger.getNameCn() + ": 增加适用机票政策(" + flightPolicy.getPolicyCode() + ").");
    em.persist(log);

    return pfp.getId();
  }

  @Override
  public int removePassengerFlightPolicy(int enterpriseId, String username, int psgId, int policyId) {
    int retCode = em.createQuery("delete from PassengerFlightPolicy o " +
            "where o.enterpriseId = :enterpriseId " +
            "and o.passenger.id = :psgId " +
            "and o.policyInfo.id = :policyId")
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("psgId", psgId)
            .setParameter("policyId", policyId)
            .executeUpdate();
    if (retCode != 1) {
      return 0;
    }

    FlightKeyCustomerPolicy flightPolicy = em.find(FlightKeyCustomerPolicy.class, policyId);
    Passenger passenger = em.find(Passenger.class, psgId);
    OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_CUSTOMER_BASIC_DATA);
    log.setOperator(username);
    log.setContent(passenger.getNameCn() + ": 移除机票政策(" + flightPolicy.getPolicyCode() + ").");
    em.persist(log);

    return retCode;
  }

  @Override
  public List<Passenger> getAll(final int enterpriseId) {
    String hql = "select o from Passenger o where o.enterpriseId = :enterpriseId order by o.nameCn";
    return em.createQuery(hql, Passenger.class)
        .setParameter("enterpriseId", enterpriseId)
        .getResultList();

  }

  @Override
  public List<Passenger> searchPsgInfo(int enterpriseId, SearchCustomerCriteria sc, List<Integer> allowedCustomerIds) {
    List<Passenger> results = null;

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Passenger> cq = cb.createQuery(Passenger.class);
    Root<Passenger> root = cq.from(Passenger.class);
    root.alias("o");

    var predicates0 = getPredicates_searchPsgInfo(enterpriseId, sc, allowedCustomerIds, cb, root);

    //计算根据条件查询得出的数据总数
    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root<Passenger> countRoot = countQuery.from(Passenger.class);
    countRoot.alias("o");

    var predicates1 = getPredicates_searchPsgInfo(enterpriseId, sc, allowedCustomerIds, cb, countRoot);

    // count items
    countQuery.select(cb.count(countRoot))
        .where(predicates1.toArray(new Predicate[predicates1.size()]));
    Long count = em.createQuery(countQuery).getSingleResult();

    //查询
    cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

    List<Order> orderByes = new ArrayList<>();
    orderByes.add(cb.asc(root.get("nameEn")));

    cq.orderBy(orderByes);

    int maxResults = count.intValue();
    if (maxResults > 0) {
      Page page = sc.getPage();
      page.setRowCount(maxResults);
      try {
        results = em.createQuery(cq)
            .setFirstResult(page.getFirstPosition())
            .setMaxResults(page.getPageSize()).getResultList();

      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }

    if (results == null) {
      results = new ArrayList<>();
    }

    return results;
  }

  private static List<Predicate> getPredicates_searchPsgInfo(int enterpriseId, SearchCustomerCriteria sc, List<Integer> allowedCustomerIds, CriteriaBuilder cb, Root<Passenger> root) {
    List<Predicate> predicates = new ArrayList<>();

    Predicate predicate = cb.conjunction();
    predicates.add(predicate);

    predicate = cb.equal(root.get(Passenger_.enterpriseId), enterpriseId);
    predicates.add(predicate);

    if (sc.getCustomerId() == 0) {
      //散客
      predicate = cb.isNull(root.get(Passenger_.customer));
      predicates.add(predicate);
    } else if (sc.getCustomerId() > 0) {
      //指定客户
      predicate = cb.equal(root.get(Passenger_.customer), sc.getCustomerId());
      predicates.add(predicate);
    }

    if (sc.getName() != null && sc.getName().trim().length() > 0) {
      predicate = cb.like(root.get(Passenger_.nameCn), "%" + sc.getName() + "%");
      predicate = cb.or(predicate, cb.like(root.get(Passenger_.nameEn), "%" + sc.getName() + "%"));
      predicates.add(predicate);
    }

    if (sc.getIdno() != null && sc.getIdno().trim().length() > 0) {
      predicate = cb.like(root.get(Passenger_.idNo), "%" + sc.getIdno() + "%");
      predicates.add(predicate);
    }

    if (sc.getMobile() != null && sc.getMobile().trim().length() > 0) {
      predicate = cb.like(root.get(Passenger_.mobile), "%" + sc.getMobile() + "%");
      predicates.add(predicate);
    }

    if (allowedCustomerIds != null && !allowedCustomerIds.isEmpty()) {
      // 只允许查看指定的企业客户的订单
      predicate = cb.in(root.get(Passenger_.customer).get("id")).value(allowedCustomerIds);
      predicates.add(predicate);
    }
    return predicates;
  }

  @Override
  public List<Passenger> searchByNameCn(int enterpriseId, int customerId, String nameCn) {
    if (customerId > 0) {
      return em.createQuery("select o from Passenger o " +
              "where o.enterpriseId = :enterpriseId and o.customer.id = :customerId and o.nameCn = :nameCn",
          Passenger.class)
          .setParameter("enterpriseId", enterpriseId)
          .setParameter("customerId", customerId)
          .setParameter("nameCn", nameCn)
          .getResultList();
    } else {
      return em.createQuery("select o from Passenger o " +
          "where o.enterpriseId = :enterpriseId and o.nameCn = :nameCn", Passenger.class)
          .setParameter("enterpriseId", enterpriseId)
          .setParameter("nameCn", nameCn)
          .getResultList();
    }
  }

  @Override
  public List<Passenger> searchByNameEn(int enterpriseId, int customerId, String nameEn) {
    if (customerId > 0) {
      return em.createQuery("select o from Passenger o " +
              "where o.enterpriseId = :enterpriseId and o.customer.id = :customerId and o.nameEn = :nameEn",
          Passenger.class)
          .setParameter("enterpriseId", enterpriseId)
          .setParameter("customerId", customerId)
          .setParameter("nameEn", nameEn)
          .getResultList();
    } else {
      return em.createQuery("select o from Passenger o " +
          "where o.enterpriseId = :enterpriseId and o.nameEn = :nameEn", Passenger.class)
          .setParameter("enterpriseId", enterpriseId)
          .setParameter("nameEn", nameEn)
          .getResultList();
    }
  }

  @Override
  public List<Passenger> searchByName(int enterpriseId, int customerId, String name) {
    if (customerId > 0) {
      return em.createQuery("select o from Passenger o " +
                      "where o.enterpriseId = :enterpriseId " +
                      "and o.customer.id = :customerId " +
                      "and (o.nameEn = :nameEn or o.nameCn = :nameCn)",
              Passenger.class)
              .setParameter("enterpriseId", enterpriseId)
              .setParameter("customerId", customerId)
              .setParameter("nameEn", name)
              .setParameter("nameCn", name)
              .getResultList();
    } else {
      return em.createQuery("select o from Passenger o " +
                      "where o.enterpriseId = :enterpriseId and (o.nameEn = :nameEn or o.nameCn = :nameCn)",
              Passenger.class)
              .setParameter("enterpriseId", enterpriseId)
              .setParameter("nameEn", name)
              .setParameter("nameCn", name)
              .getResultList();
    }
  }


  @Override
  public int savePsgInfo(Passenger psgInfo) {
    int retCode = 0;

    if (psgInfo.getId() == 0) {
      em.persist(psgInfo);
    } else {
      //检查是否有需要删除的 证件信息
      Passenger oldInfo = em.find(Passenger.class, psgInfo.getId());
      for(PassengerIdInfo oldIdInfo: oldInfo.getIdInfos()) {
        boolean existed = false;
        for(PassengerIdInfo newIdInfo: psgInfo.getIdInfos()) {
          newIdInfo.setLastUpdate(DateUtil.getCurDateTime());

          if (newIdInfo.getId() == oldIdInfo.getId()) {
            existed = true;
            break;
          }
        }

        if (!existed) {
          em.remove(oldIdInfo);
        }
      }

      //检查是否有需要删除的 常旅客信息
//      Passenger oldInfo = em.find(Passenger.class, psgInfo.getId());
      for(PassengerFfpNo oldFfp: oldInfo.getFfpNos()) {
        boolean existed = false;
        for(PassengerFfpNo newFfp: psgInfo.getFfpNos()) {
          newFfp.setLastUpdate(DateUtil.getCurDateTime());

          if (newFfp.getId() == oldFfp.getId()) {
            existed = true;
            break;
          }
        }

        if (!existed) {
          em.remove(oldFfp);
        }
      }

      psgInfo.setLastUpdate(DateUtil.getCurDateTime());
      em.merge(psgInfo);
    }

    return psgInfo.getId();
  }
}
